#pragma  once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "LockGuard.hpp"

const int defaultcap = 5; // for test

template<class T>
class BlockQueue
{
public:
    BlockQueue(int cap = defaultcap):_capacity(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }
    bool IsFull()
    {
        return _q.size() == _capacity;
    }
    bool IsEmpty()
    {
        return _q.size() == 0;
    }
    void Push(const T &in) // 生产者的
    {
        LockGuard lockguard(&_mutex);
        // pthread_mutex_lock(&_mutex); // 2. lockguard 3. 重新理解生产消费模型(代码+理论) 4. 代码整体改成多生产，多消费
        while(IsFull()) // if改成while：使用broadcast函数可以避免伪唤醒问题，因为只有不为空才能往后走，写出来的代码，具有较强的鲁棒、健壮性
        {
            // 阻塞等待
            pthread_cond_wait(&_p_cond, &_mutex); //不用担心和上面的LockGuard lockguard(&_mutex);冲突，这里虽然要先释放锁，但是获得条件变量时会重新加锁
        }

        _q.push(in);
        // if(_q.size() > _productor_water_line) pthread_cond_signal(&_c_cond);//可以自定义消费策略，下面简化一下，一消费就通知对方生产
        pthread_cond_signal(&_c_cond);//先唤醒还是先解锁都可以，如果先唤醒，那么被唤醒的线程会在锁那里等待而不是在条件变量处等待，唤醒之后立刻就释放锁也会很快响应的
        // pthread_mutex_unlock(&_mutex);
    }
    void Pop(T *out)       // 消费者的
    {
        LockGuard lockguard(&_mutex);
        // pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            // 阻塞等待
            pthread_cond_wait(&_c_cond, &_mutex);
        }

        *out = _q.front();//把消费的对象带出去
        _q.pop();
        // if(_q.size() < _consumer_water_line) pthread_cond_signal(&_p_cond);
        pthread_cond_signal(&_p_cond);
        // pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }
private:
    std::queue<T> _q;//queue是会自动扩容的，但是实现这个阻塞队列是希望有上限的
    //生产和消费无法同时进行，因为它们都使用同一个队列
    int _capacity; // 阻塞队列：_q.size() == _capacity, 满了，不能再向队列中生产；_q.size() == 0, 空，不能从队列中消费了

    pthread_mutex_t _mutex;
    //条件变量，分别互相唤醒对方
    pthread_cond_t _p_cond; // 给生产者的
    pthread_cond_t _c_cond; // 给消费者的

    // int _consumer_water_line;  // 假设水位线是_consumer_water_line = _capacity / 3 * 2
    // int _productor_water_line; // 假设_productor_water_line = _capacity / 3
};
